package com.spider.core;

import com.spider.strategy.ResponseStrategy;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * @author β世界
 * Created on 16:45
 * @Description 爬虫的客户端设置内容
 * 需要对一个客户端进行初始化设置的有:
 *  ***1.种子URL(客户端会对他进行一次单独爬取，来获取后面需要爬取的内容)
 *  2.客户端的个数(线程数，默认为10个)
 *  ***3.是否存储的cookie(多个客户端使用同一个cookie，使用第一次访问时服务器返回给的cookie)
 *  4.客户端的爬取间隔(每个线程爬取完毕休眠多久)
 *  ***5.客户端的核心处理逻辑(由用户编写，需要传递一个ProcessLogic)
 *  6.客户端请求的类型(默认情况下为GET)
 *  7.请求的编码格式(默认为UTF-8)
 *  8.是否需要进行SSL认证
 *  9.请求超时时间(默认为30s)
 *  10.是否需要持久化？(列入写入到文件/数据库等操作，默认情况下为输出到控制台)
 *  11.需要持久化的处理方式(他们都继承了一个父类，既Persistence类，这个类下有一个dispatch的方法，调用这个方法来实现对一个URL的处理)
 *  12.添加自定义结果处理策略
*/
@SuppressWarnings("unused")
public class SpiderConfig {
    private final static Logger logger = LoggerFactory.getLogger(SpiderConfig.class);

    /** 代理服务器的IP **/
    private String proxyIP = null;
    /** 代理服务器的端口 **/
    private Integer proxyPort = null;
    /** 线程数 **/
    private int threadNumber = 10;
    /** 是否是Get的请求 **/
    private boolean get = false;
    /** 爬取间隔 **/
    private long interval;
    /** 线程的休眠时间 **/
    private long keepAliveTime = 0L;
    /** 请求超时时间 **/
    private int requestTimeOut;
    /** 用户添加的header **/
    private final ArrayList<BasicNameValuePair> headers = new ArrayList<>();
    /** 请求携带的参数 **/
    private final ArrayList<BasicNameValuePair> param = new ArrayList<>();
    /** 响应结果的处理策略 **/
    private final ArrayList<ResponseStrategy> responseStrategyArrayList = new ArrayList<>();
    /** 是否处理HTTPS **/
    private boolean https;
    /** RequestConfig的设置 **/
    private final RequestConfig.Builder configBuild = RequestConfig.custom();
    /** 是否自动关闭 **/
    private boolean autoClose = true;

    private SpiderConfig() {}

    /**
     * @author β世界 on 22:15 2021/3/17
     * 开始构建spider设置
     * @return com.spider.core.SpiderConfig
     **/
    public static SpiderConfig builder(){
        return new SpiderConfig();
    }

    /***
     * @author β世界 on 16:49 2020/2/2
     * @Description 结束当前的设置，并且创建一个爬虫
     **/
    public SpiderClient create(){
        // 添加请求协议
        System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");

        // 检查代理服务器
        if(proxyPort != null && proxyIP != null){
            setProxy(proxyIP,proxyPort);
        }

        // 设置请求超时的时间
        configBuild.setConnectionRequestTimeout(requestTimeOut);
        // 构建RequestConfig
        RequestConfig requestConfig = configBuild.build();
        return new SpiderClient(this,requestConfig);
    }

    public SpiderConfig isGet(boolean get) { // 请求是否为Get
        this.get = get;
        return this;
    }

    public SpiderConfig setThreadNumber(int threadNumber) { // 爬虫的线程数
        this.threadNumber = threadNumber;
        return this;
    }

    public SpiderConfig setInterval(long interval) {
        this.interval = interval;
        return this;
    }

    public SpiderConfig setRequestTimeOut(int requestTimeOut) {
        this.requestTimeOut = requestTimeOut;
        return this;
    }

    public SpiderConfig setKeepAliveTime(long keepAliveTime) {
        this.keepAliveTime = keepAliveTime;
        return this;
    }

    public SpiderConfig addHeader(String name, String value){
        headers.add(new BasicNameValuePair(name,value));
        return this;
    }

    public SpiderConfig addParam(String key,String value){
        param.add(new BasicNameValuePair(key,value));
        return this;
    }

    /**
     *  添加用户自定义的响应处理策略
     * @author β世界 on 15:00 2021/5/10
     * @return com.spider.core.SpiderConfig
     **/
    public SpiderConfig addResponseStrategy(ResponseStrategy responseStrategy){
        responseStrategyArrayList.add(responseStrategy);
        return this;
    }

    /**
     * @author β世界 on 22:17 2021/3/17
     * 添加白名单的规则
     * @param status: 状态
     * @return com.spider.core.SpiderConfig
     **/
    public SpiderConfig addWhiteListRule(int status){
        ResponseStatusWhiteList.addRule(status);
        return this;
    }

    /**
     * @author β世界 on 15:54 2020/2/8
     * @Description 设置代理服务器
     * @param proxy:代理服务器的IP
     * @param port:服务器端口
     * @return com.spider.core.SpiderConfig
     **/
    private SpiderConfig setProxy(String proxy , int port) {
        //测试代理服务器是否可以连接
        logger.info("测试代理连接...");
        Socket ip = new Socket();
        try {
            ip.connect(new InetSocketAddress(proxy,port),20000);
            if(ip.isConnected()){
                // 代理连接成功后，我们可以通过访问google来进行测试
                HttpHost proxyHost = new HttpHost(proxy,port);
                // 添加到配置中
                configBuild.setProxy(proxyHost);
            }else{
                logger.info("代理无效！");
            }
        } catch (IOException e) {
            logger.warn("代理初始化错误！");
            e.printStackTrace();
        }

        return this;
    }

    public SpiderConfig setProxyIP(String proxyIP) {
        this.proxyIP = proxyIP;
        return this;
    }

    public SpiderConfig setProxyPort(Integer proxyPort) {
        this.proxyPort = proxyPort;
        return this;
    }

    public SpiderConfig setOutTime(int outTime){
        configBuild.setConnectTimeout(outTime);
        return this;
    }

    public boolean isHttps() {
        return https;
    }

    public SpiderConfig setHttps(boolean https) {
        this.https = https;
        return this;
    }

    public Logger getLog() {
        return logger;
    }

    public int getThreadNumber() {
        return threadNumber;
    }

    public boolean isGet(){
        return get;
    }

    public long getInterval() {
        return interval;
    }

    public int getRequestTimeOut() {
        return requestTimeOut;
    }

    public ArrayList<BasicNameValuePair> getParam() {
        return param;
    }

    public String getProxyIP() {
        return proxyIP;
    }

    public Integer getProxyPort() {
        return proxyPort;
    }

    public long getKeepAliveTime() {
        return keepAliveTime;
    }

    protected List<BasicNameValuePair> getHeaders(){
        return headers;
    }

    public ArrayList<ResponseStrategy> getResponseStrategyArrayList() {
        return responseStrategyArrayList;
    }

    public boolean isAutoClose() {
        return autoClose;
    }

    public void setAutoClose(boolean autoClose) {
        this.autoClose = autoClose;
    }

    @Override
    public String toString() {
        return "SpiderConfig{" +
                "proxyIP='" + proxyIP + '\'' +
                ", proxyPort=" + proxyPort +
                ", threadNumber=" + threadNumber +
                ", get=" + get +
                ", interval=" + interval +
                ", keepAliveTime=" + keepAliveTime +
                ", requestTimeOut=" + requestTimeOut +
                ", headers=" + headers +
                ", param=" + param +
                ", responseStrategyArrayList=" + responseStrategyArrayList +
                ", https=" + https +
                ", configBuild=" + configBuild +
                ", autoClose=" + autoClose +
                '}';
    }
}

